﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WrWpf.Controls
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WrWpf"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WrWpf;assembly=WrWpf"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Browse to and select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:CalendarDay/>
    ///
    /// </summary>
    [TemplatePart(Name = "spCalendarDay", Type = typeof(StackPanel))]
    public class CalendarDay : Control
    {
        #region Constructors

        /// <summary>
        /// Initializes the <see cref="CalendarDay"/> class.
        /// </summary>
        static CalendarDay()
        {
            //Initialize Properties
            DateProperty = DependencyProperty.Register(
                "Date",
                typeof(DateTime),
                typeof(CalendarDay),
                new PropertyMetadata(DateChangedCallback));
            
            IsTodayProperty = DependencyProperty.Register(
                "IsToday",
                typeof(bool),
                typeof(CalendarDay));
            
            ShowCheckboxesProperty = DependencyProperty.Register(
                "ShowCheckboxes",
                typeof(Visibility),
                typeof(CalendarDay),
                new PropertyMetadata(ShowCheckboxesCallback));
            
            DayPropertyKey = DependencyProperty.RegisterReadOnly(
                "Day",
                typeof(string),
                typeof(CalendarDay),
                new PropertyMetadata());

            DayProperty = DayPropertyKey.DependencyProperty;

            LunarDayPropertyKey = DependencyProperty.RegisterReadOnly(
                "LunarDay",
                typeof(string),
                typeof(CalendarDay),
                new PropertyMetadata());

            LunarDayProperty = LunarDayPropertyKey.DependencyProperty;

            IsSelectedDayPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsSelectedDay",
                typeof(bool),
                typeof(CalendarDay),
                new PropertyMetadata());

            IsSelectedDayProperty = IsSelectedDayPropertyKey.DependencyProperty;

            IsHolidayPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsHoliday",
                typeof(bool),
                typeof(CalendarDay),
                new PropertyMetadata());

            IsHolidayProperty = IsHolidayPropertyKey.DependencyProperty;
            
            IsWeekendPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsWeekend",
                typeof(bool),
                typeof(CalendarDay),
                new PropertyMetadata());

            IsWeekendProperty = IsWeekendPropertyKey.DependencyProperty;

            //To ensure that these styles are successfully united with objects of type CalendarDay
            //these classes must return a correct value from their DefaultStyleKey properties
            //For this reason, the static constructors of these classes alter the default value of 
            //that property to return the type of the class. This is accomplished by overriding the 
            //metadata of the DefaultStyleKey dependency property
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CalendarDay), new FrameworkPropertyMetadata(typeof(CalendarDay)));
            //Doesn't allow tab-stop
            //If you need to change the default value of an inherited property, the low-precedence approach 
            //is calling OverrideMetadata on the dependency property, as I showed earlier with the 
            //DefaultStyleKey property. The Control class itself sets a default true value of IsTabStop 
            //in this manner.
            CalendarDay.IsTabStopProperty.OverrideMetadata(typeof(CalendarDay), new FrameworkPropertyMetadata(false));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CalendarDay"/> class.
        /// </summary>
        /// <param name="date">The date.</param>
        public CalendarDay(DateTime date)
        {
            this.Date = date;
            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(CalendarDay_IsEnabledChanged);
        }

        #endregion

        #region Properties

        #region LunarDate
        LunarDateTime lunarDate;
        /// <summary>
        /// Gets the lunar date.
        /// </summary>
        /// <value>The lunar date.</value>
        public LunarDateTime LunarDate
        {
            get { return this.lunarDate; }
        }
        #endregion

        #region Date
        /// <summary>
        /// 
        /// </summary>
        public static readonly DependencyProperty DateProperty;

        /// <summary>
        /// Gets or sets the date.
        /// </summary>
        /// <value>The date.</value>
        public DateTime Date
        {
            get { return (DateTime)this.GetValue(DateProperty); }
            set 
            { 
                this.SetValue(DateProperty, value);
                lunarDate = LunarDateTime.FromGregorianDate(value);
                if (this.IsEnabled)
                {
                    string ld;
                    if (lunarDate.Day == 1)   //Start lunar month
                    {
                        ld = string.Concat("1/", lunarDate.Month);
                        if (lunarDate.IsLeap)
                            ld += "*";
                    }
                    else if (value.Day == 1)       //Start solor month
                    {
                        ld = string.Concat(lunarDate.Day, "/", lunarDate.Month);
                        if (lunarDate.IsLeap)
                            ld += "*";
                    }
                    else
                        ld = lunarDate.Day.ToString();
                    string s = lunarDate.TiếtKhí;
                    if (s.Length > 0)
                        ld = ld + Environment.NewLine + s;
                    this.LunarDay = ld;
                }
                else
                {
                    this.LunarDay = string.Empty;
                }
            }
        }

        static void DateChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            CalendarDay calendarDay = (CalendarDay)obj;
            calendarDay.IsWeekend = calendarDay.Date.DayOfWeek == DayOfWeek.Saturday ||
                calendarDay.Date.DayOfWeek == DayOfWeek.Sunday;
            calendarDay.Day = calendarDay.Date.Day.ToString();
        }

        #endregion

        #region IsToday
        private static readonly DependencyProperty IsTodayProperty ;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is today.
        /// </summary>
        /// <value><c>true</c> if this instance is today; otherwise, <c>false</c>.</value>
        public bool IsToday
        {
            get { return (bool)this.GetValue(IsTodayProperty); }
            set { this.SetValue(IsTodayProperty, value); }
        }
        #endregion

        #region ShowCheckboxes
        /// <summary>
        /// 
        /// </summary>
        public static readonly DependencyProperty ShowCheckboxesProperty;

        /// <summary>
        /// Gets or sets a value indicating whether [show checkboxes].
        /// </summary>
        /// <value><c>true</c> if [show checkboxes]; otherwise, <c>false</c>.</value>
        public Visibility ShowCheckboxes
        {
            get { return (Visibility)this.GetValue(ShowCheckboxesProperty); }
            set { this.SetValue(ShowCheckboxesProperty, value); }
        }

        static void ShowCheckboxesCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            CalendarDay calendarDay = (CalendarDay)obj;
        }
        #endregion

        #region Day
        private static readonly DependencyPropertyKey DayPropertyKey ;

        /// <summary>
        /// 
        /// </summary>
        public static readonly DependencyProperty DayProperty ;

        /// <summary>
        /// Gets or sets the day.
        /// </summary>
        /// <value>The day.</value>
        public string Day
        {
            get { return (string)this.GetValue(DayProperty); }
            internal set { this.SetValue(DayPropertyKey, value); }
        }
        #endregion

        #region LunarDay
        private static readonly DependencyPropertyKey LunarDayPropertyKey;

        public static readonly DependencyProperty LunarDayProperty;

        public string LunarDay
        {
            get { return (string)this.GetValue(LunarDayProperty); }
            internal set { this.SetValue(LunarDayPropertyKey, value); }
        }
        #endregion

        #region IsSelectedDay
        private static readonly DependencyPropertyKey IsSelectedDayPropertyKey ;

        private static readonly DependencyProperty IsSelectedDayProperty ;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is selected day.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is selected day; otherwise, <c>false</c>.
        /// </value>
        public bool IsSelectedDay
        {
            get { return (bool)this.GetValue(IsSelectedDayProperty); }
            internal protected set { this.SetValue(IsSelectedDayPropertyKey, value); }
        }
        #endregion

        #region IsSpecialDay
        private static readonly DependencyPropertyKey IsHolidayPropertyKey ;

        private static readonly DependencyProperty IsHolidayProperty ;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is special day.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is special day; otherwise, <c>false</c>.
        /// </value>
        public bool IsHoliday
        {
            get { return (bool)this.GetValue(IsHolidayProperty); }
            internal protected set { this.SetValue(IsHolidayPropertyKey, value); }
        }
        #endregion

        #region IsWeekend
        private static readonly DependencyPropertyKey IsWeekendPropertyKey ;

        private static readonly DependencyProperty IsWeekendProperty ;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is special day.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is special day; otherwise, <c>false</c>.
        /// </value>
        public bool IsWeekend
        {
            get { return (bool)this.GetValue(IsWeekendProperty); }
            private set { this.SetValue(IsWeekendPropertyKey, value); }
        }
        #endregion

        #endregion

        #region Private functions

        /// <summary>
        /// Handles the IsEnabledChanged event of the CalendarDay control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        void CalendarDay_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.OldValue)
            {
                this.LunarDay = string.Empty;
            }
        }

        #endregion
    }

    public struct LunarDateTime
    {
        private static readonly string[] can_Names =  { "Giáp", "Ất", "Bính", "Đinh", "Mậu", "Kỷ", "Canh", "Tân", "Nhâm", "Quý" };
        private static string[] chi_Names = new string[] { "Tý", "Sửu", "Dần", "Mão", "Thìn", "Tỵ", "Ngọ", "Mùi", "Thân", "Dậu", "Tuất", "Hợi" };

        public int Year;
        public int Month;
        public int Day;
        //private DateTime gregorius;
        private int dayNumber;
        public bool IsLeap;
        public double TimeZone;

        public LunarDateTime(int year, int month, int day, bool isLeap, double timeZone)
        {
            this.Year = year;
            this.Month = month;
            this.Day = day;
            this.IsLeap = isLeap;
            this.TimeZone = timeZone;
            DateTime gregorius = CalendarMonth.Lunar2Solar(year, month, day, isLeap, timeZone);
            dayNumber = CalendarMonth.Gregorius2Julius(gregorius);
            string s = this.TiếtKhí;
        }

        public static LunarDateTime FromGregorianDate(DateTime dt, double timeZone)
        {
            return CalendarMonth.Solar2Lunar(dt, timeZone);
        }

        /// <summary>
        /// Get lunar date from gregorian date with time zone is 7h00 (Viet Nam timezone)
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <returns></returns>
        public static LunarDateTime FromGregorianDate(DateTime dt)
        {
            return CalendarMonth.Solar2Lunar(dt, 7.0);
        }

        public string YearName
        {
            get
            {
                int year = this.Year;
                return string.Concat(can_Names[(year + 6) % 10], " ", chi_Names[(year + 8) % 12]);
            }
        }

        public string MonthName
        {
            get
            {
                int year = this.Year;
                int month = this.Month;
                return string.Concat(can_Names[(year * 12 + month + 3) % 10], " ", chi_Names[(month + 1) % 12], this.IsLeap ? " nhuận" : string.Empty);
            }
        }

        public string DayName
        {
            get
            {
                return string.Concat(can_Names[(dayNumber + 9) % 10], " ", chi_Names[(dayNumber + 1) % 12]);
            }
        }

        private static readonly string[] TIETKHI = new string[]{
            "Xuân phân", "Thanh minh", "Cốc vũ", "Lập hạ", "Tiểu mãn", "Mang chủng",
            "Hạ chí", "Tiểu thử", "Đại thử", "Lập thu", "Xử thử", "Bạch lộ",
            "Thu phân", "Hàn lộ", "Sương giáng", "Lập đông", "Tiểu tuyết", "Đại tuyết",
            "Đông chí", "Tiểu hàn", "Đại hàn", "Lập xuân", "Vũ Thủy", "Kinh trập"
        };
        public string TiếtKhí
        {
            get
            {
                int index = CalendarMonth.GetSunLongitude24(this.dayNumber, 7.0);
                int index1 = CalendarMonth.GetSunLongitude24(this.dayNumber + 1, 7.0);
                if (index != index1)
                    return TIETKHI[index1];
                else
                    return string.Empty;
            }
        }
    }
}
